package com.easyedu.dean.service;

import com.easyedu.core.constant.Constant;
import com.easyedu.core.dao.ClassRoomDao;
import com.easyedu.core.dao.InstituteDao;
import com.easyedu.core.dao.OpenCourseDao;
import com.easyedu.core.dao.OpenCourseTimeMidDao;
import com.easyedu.core.dao.OpenCourseTimeVersionDao;
import com.easyedu.core.dao.ResidenceHallsDao;
import com.easyedu.core.dao.StudentDao;
import com.easyedu.core.entity.dto.ClassRoomBuildingInfo;
import com.easyedu.core.entity.dto.OpenCourseInfoByVersion;
import com.easyedu.core.entity.po.Classroom;
import com.easyedu.core.entity.po.Institute;
import com.easyedu.core.entity.po.OpenCourse;
import com.easyedu.core.entity.po.OpenCourseTimeMid;
import com.easyedu.core.entity.po.OpenCourseTimeVersion;
import com.easyedu.core.entity.po.ResidenceHalls;
import com.easyedu.core.exception.EasyEduException;
import com.easyedu.core.manager.TimeTableVersionManager;
import com.easyedu.dean.entity.dto.response.RespQueryClassroomDistance;
import com.easyedu.dean.entity.dto.response.RespQueryConflict;
import com.easyedu.dean.entity.dto.response.RespQueryStudentDistance;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;

/**
 * @author mengranliu
 * @date 2022/3/30
 * @describe 评价算法业务逻辑
 */
@Service
public class EvaluateService {

     private final OpenCourseTimeVersionDao openCourseTimeVersionDao;

     private final OpenCourseTimeMidDao openCourseTimeMidDao;

     private final OpenCourseDao openCourseDao;

     private final ClassRoomDao classRoomDao;

     private final TimeTableVersionManager timeTableVersionManager;

     private final InstituteDao instituteDao;

     private final StudentDao studentDao;

     private final ResidenceHallsDao residenceHallsDao;

     public EvaluateService(OpenCourseTimeVersionDao openCourseTimeVersionDao,
             OpenCourseTimeMidDao openCourseTimeMidDao, OpenCourseDao openCourseDao, ClassRoomDao classRoomDao,
             TimeTableVersionManager timeTableVersionManager, InstituteDao instituteDao, StudentDao studentDao,
             ResidenceHallsDao residenceHallsDao) {
          this.openCourseTimeVersionDao = openCourseTimeVersionDao;
          this.openCourseTimeMidDao = openCourseTimeMidDao;
          this.openCourseDao = openCourseDao;
          this.classRoomDao = classRoomDao;
          this.timeTableVersionManager = timeTableVersionManager;
          this.instituteDao = instituteDao;
          this.studentDao = studentDao;
          this.residenceHallsDao = residenceHallsDao;
     }

     public RespQueryClassroomDistance calculateClassRoomBalance(String versionId) {
          //查询id是否存在
          OpenCourseTimeVersion versionInfo = openCourseTimeVersionDao.getByVersionId(versionId);
          if (versionInfo == null || versionInfo.getId() == 0) {
               throw new EasyEduException(1999, "未查询到版本");
          }
          //根据版本获取所有的已排课课程
          List<OpenCourseTimeMid> openCourseTimeMids =
                  openCourseTimeMidDao.getByVersionId(versionInfo.getOpenCourseTimeMidVersionId());

          Set<String> userClassroomSet = new HashSet<>();
          //保存教室利用率
          List<Float> classRoomBalance = new ArrayList<>();
          //计算教室的利用率
          for (OpenCourseTimeMid openCourseTimeMid : openCourseTimeMids) {
               String classroom = openCourseTimeMid.getClassroom();
               long openWeek = openCourseTimeMid.getOpenWeek();
               long openDay = openCourseTimeMid.getOpenDay();
               long openLesson = openCourseTimeMid.getOpenLesson();
               String key = classroom + openWeek + openDay + openLesson;
               //计算教室占用率

               //获取课程人数
               OpenCourse openCourse = openCourseDao.getOpenCourseById(openCourseTimeMid.getOpenCoursesId());

               //获取教室容量
               Classroom classroomByName = classRoomDao.getClassroomByName(classroom);
               classRoomBalance.add((float) openCourse.getCapacity() / classroomByName.getSeats());
               userClassroomSet.add(classroom);
          }
          //计算教室利用率的平均值
          Double collect = classRoomBalance.stream().collect(Collectors.averagingDouble(Float::doubleValue));

          //获取教室数量
          int classRoomCount = classRoomDao.getClassRoomCount();

          return RespQueryClassroomDistance.builder().usageRate(collect).usageClassroomNum(userClassroomSet.size())
                  .unUsageClassroomNum(classRoomCount - userClassroomSet.size()).totalClassroomNum(classRoomCount)
                  .build();
     }
     public RespQueryStudentDistance calculateStudentDistance(String versionId) {
          //查询id是否存在
          OpenCourseTimeVersion versionInfo = openCourseTimeVersionDao.getByVersionId(versionId);
          if (versionInfo == null || versionInfo.getId() == 0) {
               throw new EasyEduException(1999, "未查询到版本");
          }
          //存储专业总路程信息
          double TotalDistance = 0;
          long totalStudent = 0;
          //获取所有的专业
          List<Institute> instituteList = instituteDao.getInstitute();
          for (Institute institute : instituteList) {
               //获取专业宿舍坐标
               List<ResidenceHalls> departmentHalls = residenceHallsDao.getDepartmentHalls(institute.getDepartmentId());
               if (departmentHalls.isEmpty()) {
//                    throw new EasyEduException(1999,"未找到专业对应的宿舍");
                    continue;
               }
               //获取每个年级的课程
               for (String schoolYear : Constant.SCHOOL_YEAR_ARR) {
                    //获取专业人数
                    long studentCount = getStudentCount(institute.getInstituteId(), institute.getDepartmentId(),
                            versionInfo.getOpenYear(), schoolYear, versionInfo.getSemester());
                    totalStudent += studentCount;
                    //获取每个年级的课程
                    List<OpenCourseInfoByVersion> openCourseInfo = openCourseDao
                            .getOpenCourseByGradeAndDepartment(versionInfo.getOpenYear(), schoolYear,
                                    versionInfo.getSemester(), institute.getInstituteId(), institute.getDepartmentId(),
                                    versionInfo.getOpenCourseTimeMidVersionId());
                    //按照周一到周五处理课程时间
                    Map<Integer, Map<Integer, List<OpenCourseInfoByVersion>>> weekInfo = new TreeMap<>();
                    for (OpenCourseInfoByVersion info : openCourseInfo) {
                         int openDay = info.getOpenDay();
                         int openLesson = info.getOpenLesson();
                         Map<Integer, List<OpenCourseInfoByVersion>> dayMap =
                                 weekInfo.getOrDefault(openDay, new TreeMap<>());
                         List<OpenCourseInfoByVersion> infoList = dayMap.getOrDefault(openLesson, new ArrayList<>());
                         infoList.add(info);
                         dayMap.put(openLesson, infoList);
                         weekInfo.put(openDay, dayMap);
                    }
                    //计算
                    for (int i = 1; i <= 5; i++) {
                         if (!weekInfo.containsKey(i)) {
                              continue;
                         }
                         Map<Integer, List<OpenCourseInfoByVersion>> dayMap = weekInfo.get(i);
                         //今天学生移动的总距离
                         double studentDistance = 0;
                         boolean hasFirst = false;
                         //上节课教室坐标，用于计算教室间移动距离（TODO 考虑多课程）
                         double beforeCourseX = 0;
                         double beforeCourseY = 0;
                         //上节课学生的移动距离
                         double beforeCourseDistance = 0;
                         for (int j = 1; j <= 4; j++) {
                              //本节课坐标，用于计算教室间移动距离
                              double thisCourseX = 0;
                              double thisCourseY = 0;
                              if (!dayMap.containsKey(j)) {
                                   continue;
                              }
                              //1到4节
                              List<OpenCourseInfoByVersion> infoList = dayMap.get(j);

                              //这门课程学生到宿舍的总距离
                              double courseDistance = 0;
                              if (!infoList.isEmpty()) {
                                   //计算每一节课有多少人上，这里暂时采用平均来做，实际上应该按照课程人数来做
                                   long lessonStudentNumber = studentCount / infoList.size();
                                   //获取课程所在教室的位置信息
                                   for (OpenCourseInfoByVersion info : infoList) {
                                        String classroom = info.getClassroom();
                                        ClassRoomBuildingInfo classroomInfo =
                                                classRoomDao.getClassroomInfoByName(classroom);
                                        ResidenceHalls residenceHalls = departmentHalls.get(0);
                                        //计算教室到宿舍的距离
                                        double residenceHallsX = residenceHalls.getX();
                                        double residenceHallsY = residenceHalls.getY();
                                        double classroomInfoX = classroomInfo.getX();
                                        double classroomInfoY = classroomInfo.getY();
                                        double distance = Math.sqrt(Math.pow(residenceHallsX - classroomInfoX, 2) + Math
                                                .pow(residenceHallsY - classroomInfoY, 2));
                                        //计算每一节课的人需要走的距离
                                        double lessonDistace = distance * lessonStudentNumber;
                                        courseDistance += lessonDistace;
                                        beforeCourseX = classroomInfoX;
                                        beforeCourseY = classroomInfoY;
                                   }
                              }
                              if (j == 1 || j == 3) {
                                   //第一大节和第三大节一定需要从宿舍到教室
                                   studentDistance += courseDistance;
                                   beforeCourseDistance = courseDistance;
                                   hasFirst = true;
                              } else {
                                   //第一三大节课没课程
                                   beforeCourseX = 0;
                                   beforeCourseY = 0;
                                   hasFirst = false;
                              }
                              if (j == 2 || j == 4) {
                                   if (hasFirst) {
                                        //第一大节和第二大节之间的距离
                                        studentDistance += Math.sqrt(Math.pow(beforeCourseX - thisCourseX, 2) + Math
                                                .pow(beforeCourseY - thisCourseY, 2)) * studentCount;
                                        //回宿舍的距离
                                        studentDistance += courseDistance;
                                   } else {
                                        //只有第二大节就是从宿舍来再从宿舍回去
                                        studentDistance += courseDistance * 2;
                                   }
                              } else {
                                   if (hasFirst) {
                                        //只有第一大节
                                        studentDistance += beforeCourseDistance;
                                   }
                              }
                         }
                         TotalDistance += studentDistance;
                    }
               }

          }

          //平均路程
          double averageDistance = TotalDistance / totalStudent;
          return new RespQueryStudentDistance(TotalDistance, averageDistance, TotalDistance * 111,
                  averageDistance * 111, totalStudent);
     }
     private Long getStudentCount(long instituteId, long departmentId, int openYear, String schoolYear,
             String semester) {
          int grade;
          switch (schoolYear) {
               case "一年级": {
                    grade = openYear;
                    break;
               }
               case "二年级": {
                    grade = openYear - 1;
                    break;
               }
               case "三年级": {
                    grade = openYear - 2;
                    break;
               }
               case "四年级": {
                    grade = openYear - 3;
                    break;
               }
               default: {
                    throw new EasyEduException(1999, "年级不存在");
               }

          }
          if (!semester.equals("第一学期")) {
               grade--;
          }
          return studentDao.getStudentNumberByInstitute(instituteId, departmentId, String.valueOf(grade));
     }

     /**
      * 计算课程冲突
      *
      * @param versionId 版本id
      * @return 课程冲突
      */
     public RespQueryConflict calculateConflict(String versionId) {
          //查询id是否存在
          OpenCourseTimeVersion versionInfo = openCourseTimeVersionDao.getByVersionId(versionId);
          if (versionInfo == null || versionInfo.getId() == 0) {
               throw new EasyEduException(1999, "未查询到版本");
          }
          //根据版本获取所有的已排课课程
          List<OpenCourseTimeMid> openCourseTimeMids =
                  openCourseTimeMidDao.getByVersionId(versionInfo.getOpenCourseTimeMidVersionId());
          //存储教师时间冲突
          Map<String, String> teacherTimeConflict = new HashMap<>();
          //存储教师地点冲突
          Map<String, String> teacherClassroomConflict = new HashMap<>();
          RespQueryConflict resp = new RespQueryConflict();
          openCourseTimeMids.forEach(openCourseTimeMid -> {
               String classroom = openCourseTimeMid.getClassroom();
               String courseNumber = openCourseTimeMid.getCourseNumber();
               int openDay = openCourseTimeMid.getOpenDay();
               int openWeek = openCourseTimeMid.getOpenWeek();
               int openLesson = openCourseTimeMid.getOpenLesson();
               String timeKey =
                       courseNumber + String.valueOf(openDay) + String.valueOf(openWeek) + String.valueOf(openLesson);
               if (teacherTimeConflict.containsKey(timeKey)) {
                    resp.setTimeConflict(true);
               }
               String classroomKey =
                       courseNumber + String.valueOf(openDay) + String.valueOf(openWeek) + String.valueOf(openLesson)
                               + classroom;
               if (teacherClassroomConflict.containsKey(classroomKey)) {
                    resp.setClassroomConflict(true);
               }
               teacherTimeConflict.put(timeKey, timeKey);
               teacherClassroomConflict.put(classroomKey, classroomKey);
          });
          return resp;
     }
}
